package cn.wen.java.io;

import java.io.*;

import org.apache.log4j.Logger;

import cn.wen.java.utils.Assert;

/**
 * Title：Java IO 辅助类
 * <p>
 * Description：
 * <p>
 * Copyright： Copyright (c) 2010-2012
 * 
 * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
 * @version Revision：1.0 Date：2010/12/6 description：初完成。
 *          <P>
 *          Revision：1.1 Date：2010/12/9 description：移除关闭流方法，增加关闭IO资源。
 *          <P>
 *          Revision：1.2 Date：2011/7/6 description：提供对完成拷贝后的流资源管理（是否关闭）。 移除copy(String, Write)方法。
 * 
 * @see org.apache.commons.io.IOUtils
 */
public class HelperOfIO {

    private static final Logger log = Logger.getLogger(HelperOfIO.class);

    /**
     * 晋级量
     */
    private static final long UPGRADE = 1024;
    /**
     * B（Byte，字节）参考量
     */
    public static final long SIZE_B = 1;
    /**
     * KB（Kilobyte，千字节）参考量
     */
    public static final long SIZE_KB = SIZE_B * UPGRADE;
    /**
     * MB（Megabyte，兆字节）参考量
     */
    public static final long SIZE_MB = SIZE_KB * UPGRADE;
    /**
     * GB（Gigabyte，吉字节）参考量
     */
    public static final long SIZE_GB = SIZE_MB * UPGRADE;
    /**
     * TB（Trillionbyte，太字节）参考量
     */
    public static final long SIZE_TB = SIZE_GB * UPGRADE;
    /**
     * PB（Petabyte，拍字节）参考量
     */
    public static final long SIZE_PB = SIZE_TB * UPGRADE;
    /**
     * EB（Exabyte，艾字节）参考量
     */
    public static final long SIZE_EB = SIZE_PB * UPGRADE;
    /**
     * ZB（Zettabyte，泽字节）参考量——注：数值过大， 无法使用长整型表示。
     */
    public static final long SIZE_ZB = SIZE_EB * UPGRADE;
    /**
     * YB（Yottabyte，尧字节）参考量——注：数值过大， 无法使用长整型表示。
     */
    public static final long SIZE_YB = SIZE_ZB * UPGRADE;
    /**
     * BB（Brontobyte）参考量——注：数值过大， 无法使用长整型表示。
     */
    public static final long SIZE_BB = SIZE_YB * UPGRADE;

    /**
     * 缓冲大小
     */
    public static final int BUFFER_SIZE = 2048;

    // ---------------------------------------------------------------------
    // Copy methods for java.io.InputStream / java.io.OutputStream
    // ---------------------------------------------------------------------

    /**
     * <pre>
     * Copy the contents of the given InputStream to the given OutputStream.
     * 
     * PS: This method is not responsible for closing the stream,
     *   which is equivalent <code> copy(is, os, false); </ code>
     * </pre>
     * 
     * @param in the stream to copy from
     * @param out the stream to copy to
     * @return the number of bytes copied
     * @throws IOException in case of I/O errors
     */
    public static long copy(InputStream in, OutputStream out) throws IOException {
	Assert.isNotNull(in, "No InputStream specified");
	Assert.isNotNull(out, "No OutputStream specified");

	long byteCount = 0;
	int bytesRead = -1;
	byte[] buffer = new byte[BUFFER_SIZE];

	while ((bytesRead = in.read(buffer)) != -1) {
	    out.write(buffer, 0, bytesRead);
	    byteCount += bytesRead;
	}

	// out.flush();
	return byteCount;
    }

    /**
     * <pre>
     * Copy the contents of the given InputStream to the given OutputStream.
     * </pre>
     * 
     * @param in the stream to copy from
     * @param out the stream to copy to
     * @param isClose completed, whether to close the stream
     * @return the number of bytes copied
     * @throws IOException in case of I/O errors
     */
    public static long copy(InputStream in, OutputStream out, boolean isClose) throws IOException {
	try {
	    return copy(in, out);
	}
	catch (IOException e) {
	    throw e;
	}
	finally {
	    if (isClose) {
		closes(in, out);
	    }
	}
    }

    // ---------------------------------------------------------------------
    // Copy methods for java.io.Reader / java.io.Writer
    // ---------------------------------------------------------------------

    /**
     * <pre>
     * Copy the contents of the given Reader to the given Writer.
     * 
     * PS: This method is not responsible for closing the stream,
     *   which is equivalent <code> copy(in, out, false); </ code>
     * </pre>
     * 
     * >
     * 
     * @param in the Reader to copy from
     * @param out the Writer to copy to
     * @return the number of characters copied
     * @throws IOException in case of I/O errors
     */
    public static long copy(Reader in, Writer out) throws IOException {
	Assert.isNotNull(in, "No Reader specified");
	Assert.isNotNull(out, "No Writer specified");

	long byteCount = 0;
	int bytesRead = -1;
	char[] buffer = new char[BUFFER_SIZE];

	while ((bytesRead = in.read(buffer)) != -1) {
	    out.write(buffer, 0, bytesRead);
	    byteCount += bytesRead;
	}

	// out.flush();
	return byteCount;
    }

    /**
     * <pre>
     * Copy the contents of the given Reader to the given Writer.
     * </pre>
     * 
     * @param in the Reader to copy from
     * @param out the Writer to copy to
     * @param isClose completed, whether to close the stream
     * @return the number of characters copied
     * @throws IOException in case of I/O errors
     */
    public static long copy(Reader in, Writer out, boolean isClose) throws IOException {
	try {
	    return copy(in, out);
	}
	catch (IOException e) {
	    throw e;
	}
	finally {
	    if (isClose) {
		closes(in, out);
	    }
	}
    }

    // ---------------------------------------------------------------------
    // Other methods
    // ---------------------------------------------------------------------

    /**
     * 关闭IO资源（假如参数同样实现了Flushable，会在close之前抢先flush。）
     * 
     * @see #flushes(Flushable...)
     * @param closeables 可关闭的资源集
     */
    public static void closes(Closeable... closeables) {
	try {
	    for (Closeable closer : closeables) {
		if (closer instanceof Flushable) {
		    flushes((Flushable) closer);
		}

		closer.close();
	    }
	}
	catch (IOException e) {
	    log.error(e.getMessage(), e);
	}
    }

    /**
     * 将所有已缓冲输出写入底层流。
     * 
     * @param flushables 可清理的资源集
     */
    public static void flushes(Flushable... flushables) {
	try {
	    for (Flushable flusher : flushables) {
		flusher.flush();
	    }
	}
	catch (IOException e) {
	    log.error(e.getMessage(), e);
	}
    }

    /**
     * 直观表示文件大小的字符串
     * 
     * @param size 文件长度
     * @return 带容量单位的表示法。
     */
    public static String makeSize(long size) {
	if (size <= SIZE_B) return "1 Byte";
	else if (size < SIZE_KB) return size + " Byte";
	else if (size < SIZE_MB) return _makeSize(size, SIZE_KB) + " KB";
	else if (size < SIZE_GB) return _makeSize(size, SIZE_MB) + " MB";
	else if (size < SIZE_TB) return _makeSize(size, SIZE_GB) + " GB";
	else if (size < SIZE_PB) return _makeSize(size, SIZE_TB) + " TB";
	return null;
    }

    /**
     * MakeSize的最小单元计算器
     * 
     * @param size 容量
     * @param multiplicator 参考量
     * @return 转换成最接近容量表示法
     */
    private static double _makeSize(long size, long multiplicator) {
	double res = Double.valueOf(size) / multiplicator;
	res = (long) (res * 1000) / 1000D;
	return res;
    }

}
